/****************************************************************************
*                                                                           *
* Copyright (c) RSC. All rights reserved(2010).								*
* Developers: Marius Ionescu, Cristian Bojinovici   						*
*                                                                           *
****************************************************************************/

using System;
using System.Collections;
using System.Data;
using System.Drawing;
using System.Web.Security;
using System.Web.UI.WebControls;
using NS_Controls;
using System.Web.Services;
using System.Web.Script.Services;

public partial class DomeniiAparate : SessionPageBase
{
	private string PostBackCause() { return this.Request.Form["__EVENTTARGET"]; }
	private System.Collections.Generic.List<int> editedRowIndexes = null;
	private void BackupEditedRowIndexes()
	{
		if (egvGroups.CurrentlyEditedRowsIndexes != null)
		{
			editedRowIndexes = new System.Collections.Generic.List<int>(egvGroups.CurrentlyEditedRowsIndexes.Count);
			editedRowIndexes.AddRange(egvGroups.CurrentlyEditedRowsIndexes);
		}
	}

	protected void Page_Load(object sender, EventArgs e)
    {
		output.Text = ""; //clear previous contents
		BackupEditedRowIndexes();
		if (DebugMode) { ShowRowStates("On PageLoad: "); DumpDataTableBegin(); this.PreRender += new EventHandler(DomeniiAparate_PreRender); if (IsPostBack) SaveOriginalSessionDataSet((this.Session[egvGroups.SessionKey] as DataSet).Tables[egvGroups.SessionKeyIndex]); }
		if (IsPostBack && PostBackCause().Contains("$egvGroups$") && PostBackCause().EndsWith("$lbEdit")) //Finish Editing
		{
			this.PreRender += new EventHandler(DomeniiAparate_PreRender);
			SaveOriginalSessionDataSet(LoadedGroupData);
		}
		try
        {
			if (!IsPostBack)
			{
				LoadData();
				if (DebugMode) SaveOriginalSessionDataSet((this.Session[egvGroups.SessionKey] as DataSet).Tables[egvGroups.SessionKeyIndex]);
				string tabRequest;
				if (!string.IsNullOrEmpty(tabRequest = Request.QueryString["view"]))
				{
					tabRequest = tabRequest.ToUpper();
					topSideTabs.ActiveTabIndex = tabRequest.Equals("D") ? 0 :
						tabRequest.Equals("G") ? 1 :
						tabRequest.Equals("A") || tabRequest.Equals("M") ? 2 : topSideTabs.ActiveTabIndex;
				}
			}
		}
        catch (Exception ex)
        {
            this.SetMessage(ex.ToString(), true);
        }
		//if (DebugMode)
		//{
		//    DumpDataTableBegin();
		//    this.PreRenderComplete += new EventHandler(DomeniiAparate_PreRenderComplete);
		//}
	}
	void DomeniiAparate_PreRender(object sender, EventArgs e)
	{
		if(DebugMode) ShowRowStates("On PreRender: ");
		CheckGridState();
	}
	void DomeniiAparate_PreRenderComplete(object sender, EventArgs e)
	{
		if (DebugMode) DumpDataTableEnd();
	}

	private void LoadData()
	{
		LoadDomainData();
		RefreshGroupData(true);
		LoadMachineData();
	}

	private void LoadDomainData()
	{
		//TODO? could replace with "Laborator_Generic_Select"
		DataTable domains = Hipocrate.DB.DBConnection.GetData("Laborator_Domain_Select", new object[] { }).Tables[0];
		rptDomains.DataSource = domains; //GetTestDomains();
		rptDomains.DataBind();
		int nCount = domains.Rows.Count;
		noDomainsYet.Visible = (nCount == 0);
		if (nCount == 1)
			DomainsCount.Text = "Un domeniu existent.";
		else if (nCount > 1)
			DomainsCount.Text = nCount.ToString() + (nCount%100 >= 20 ? " de" : "") + " domenii existente.";
	}

	private DataSet RawGroupDataFromDb()
	{
		//TODO? could replace with "Laborator_Generic_Select"
		DataSet groupSet = Hipocrate.DB.DBConnection.GetData("Laborator_Group_Select", new object[] { });
		groupSet.Tables[egvGroups.SessionKeyIndex].Columns["Name"].AllowDBNull = true;
		return groupSet;
	}
	private DataTable LoadedGroupData
	{
		get { return this.ViewState["LoadedGroupData"] as DataTable; }
		set
		{
			DataTable myCopy = value.Clone(); //otherwise, any changes made on the "value" object would be mirrored within LoadedGroupData too, which would defeat the purpose!
			#region make sure no columns trigger an error
			foreach (DataColumn c in myCopy.Columns)
			{
				c.ReadOnly = false;
				c.AllowDBNull = true;
			}
			#endregion
			#region copy rows
			foreach (DataRow r in value.Rows)
			{
				DataRow copiedRow = myCopy.NewRow();
				//turns out "r.ItemArray.CopyTo(copiedRow.ItemArray, 0)" doesn't really work... 
				for (int i = 0; i < myCopy.Columns.Count; ++i)
					copiedRow[i] = r[i];
				myCopy.Rows.Add(copiedRow);
			}
			#endregion
			this.ViewState["LoadedGroupData"] = myCopy;
		}
	}
	private void RefreshGroupData(bool readFromDb)
	{
		egvGroups.SessionKey = "lab_Groups";
		egvGroups.SessionKeyIndex = 0; //index of the dataTable to use
		if (readFromDb)
		{
			#region retrieve data from db and merge with any updates that might have happened.
			DataSet groupSet = RawGroupDataFromDb();
			if ((LoadedGroupData == null) || readFromDb) LoadedGroupData = groupSet.Tables[egvGroups.SessionKeyIndex];
			if ((this.Session[egvGroups.SessionKey] != null) && (egvGroups.CurrentlyEditedRowsIndexes != null))
			{
				//should update the dataset to know the input from the user...
				#region Print current Stack Trace (only in "debug" mode)
				System.Diagnostics.StackTrace info = new System.Diagnostics.StackTrace();
				string trace = info.ToString();
				if (trace.Contains("RaisePostBackEvent")) trace = trace.Replace("RaisePostBackEvent", "RaisePostBackEvent<!--") + "--><br/>";
				else if (trace.Contains("ProcessRequestWithNoAssert")) trace = trace.Replace("ProcessRequestWithNoAssert", "ProcessRequestWithNoAssert<!--") + "--><br/>";
				if (DebugMode) output.Text += "Refreshing while editing... @: " + trace;
				#endregion

				#region save old data (containing user input)
				DataSet oldValues = this.Session[egvGroups.SessionKey] as DataSet;
				DataView oldView = oldValues.Tables[egvGroups.SessionKeyIndex].DefaultView;
				System.Collections.Generic.Dictionary<object, object[]> editedValues = new System.Collections.Generic.Dictionary<object, object[]>(egvGroups.CurrentlyEditedRowsIndexes.Count);
				System.Collections.Generic.List<object> keys = new System.Collections.Generic.List<object>(egvGroups.CurrentlyEditedRowsIndexes.Count);
				foreach (int i in editedRowIndexes)
				{
					keys.Add(oldView[i][egvGroups.DataKeyNames[0]]);
					editedValues[oldView[i][egvGroups.DataKeyNames[0]]] = oldView[i].Row.ItemArray;
				}
				#endregion
				//update CERI too!
				egvGroups.CurrentlyEditedRowsIndexes.Clear();
				#region for each row, if its id was in edit, copy values from the above Dictionary
				DataTable newTable = groupSet.Tables[egvGroups.SessionKeyIndex];
				for (int rowNumber = 0; rowNumber < newTable.Rows.Count; ++rowNumber)
					if (keys.Contains(newTable.Rows[rowNumber][egvGroups.DataKeyNames[0]]))
					{
						egvGroups.CurrentlyEditedRowsIndexes.Add(rowNumber);
						DataRow row = newTable.Rows[rowNumber];
						//editedValues[row[egvGroups.DataKeyNames[0]]].CopyTo(row.ItemArray, 0);
						for (int i = 0; i < newTable.Columns.Count; ++i)
							if (!newTable.Columns[i].ReadOnly)
								row[i] = editedValues[row[egvGroups.DataKeyNames[0]]][i];
						if (row.RowState != DataRowState.Modified)
							output.Text += "CurrentlyEditedRow not seen as Modified...";
						//else
						//    output.Text += "(CurrentlyEditedRow was Modified.)";
					}
				#endregion
			}
			else if (DebugMode) //explain what's going on
			{
				if (this.Session[egvGroups.SessionKey] == null)
					output.Text += "[First time reading from the db.]";
				if (egvGroups.CurrentlyEditedRowsIndexes == null)
					output.Text += "No rows were in edit, so no data was changed in the DS!";
			}
			this.Session[egvGroups.SessionKey] = groupSet;
			#endregion
		}
		#region visible effects
		DataView groups = (this.Session[egvGroups.SessionKey] as DataSet).Tables[egvGroups.SessionKeyIndex].DefaultView;
		//? groups.Table.Columns["id"].ReadOnly = true;
		egvGroups.HiddenColumns = new string[] { "ID" };
		egvGroups.DataSource = groups;
		egvGroups.DataBind();
		int nCount = groups.Count;
		//noGroupsYet.Visible = (nCount == 0);
		if (nCount == 1)
			GroupsCount.Text = "Un grup de analize gasit.";
		else if (nCount > 1)
			GroupsCount.Text = nCount.ToString() + (nCount%100 >= 20 ? " de" : "") + " grupuri de analize gasite.";
		//HideIdColumn();
		#endregion
	}

	private void LoadMachineData()
	{
		//TODO? could replace with "Laborator_Generic_Select"
		DataTable machines = Hipocrate.DB.DBConnection.GetData("Laborator_Machine_Select", new object[] { }).Tables[0];
		rptMachines.DataSource = machines;
		rptMachines.DataBind();
		int nCount = machines.Rows.Count;
		noMachinesYet.Visible = (nCount == 0);
		if (nCount == 1)
			MachinesCount.Text = "Un singur aparat in baza de date.";
		else if (nCount > 1)
			MachinesCount.Text = nCount.ToString() + (nCount%100 >= 20 ? " de" : "") + " tipuri de aparate in baza de date.";
	}

	#region obsolete
	private static readonly int GroupIdColumnIndex = 1;
	private void HideIdColumn()
	{
		if (GroupIdColumnIndex > 0)
			throw new ApplicationException("How did you manage to call HideIdColumn? :p");
		//begvGroups.HeaderRow.Cells.RemoveAt(GroupIdColumnIndex);
		foreach (GridViewRow row in /*b*/egvGroups.Rows)
		{
			(row.Cells[2].Controls[0] as TextBox).ToolTip = (row.Cells[GroupIdColumnIndex].Controls[0] as TextBox).Text;
			row.Cells.RemoveAt(GroupIdColumnIndex);
			row.Cells[1].BackColor = System.Drawing.Color.LightBlue;
		}
		//but... how to remove the cell from the "insert" row?
	}

	private string[] GetTestDomains()
	{
		if (GroupIdColumnIndex > 0)
			throw new ApplicationException("How did you manage to call GetTestDomains? :p");
		Random rng = new Random();
		int nCount = rng.Next(100, 150);
		string[] result = new string[nCount];
		for (int i = 0; i < nCount; ++i)
		{
			int len = rng.Next(6, 11);
			char[] contents = new char[len];
			for (int j = 0; j < len; ++j)
				contents[j] = GetRandomChar(rng, j == 0, j == (len - 1));
			result[i] = new string(contents);
		}
		return result;
	}

	char[] letters = null;
	char[] all = null;
	char[] finalizers = null;
	private char GetRandomChar(Random rng, bool isFirst, bool isLast)
	{
		if (GroupIdColumnIndex > 0)
			throw new ApplicationException("How did you manage to call GetRandomChar? :p");
		if (letters == null)
		{
			letters = new char[('Z' - 'A' + 1) * 2];
			all = new char[letters.Length + 7];
			finalizers = new char[letters.Length / 2 + 3];
			int index = 0;
			for (char c = 'a'; c <= 'z'; ++c)
			{
				finalizers[index] = all[index] = letters[index] = c;
				++index;
			}
			for (char c = 'A'; c <= 'Z'; ++c)
			{
				all[index] = letters[index] = c;
				++index;
			}
			all[index] = finalizers[index - letters.Length / 2] = '.';
			++index;
			all[index] = finalizers[index - letters.Length / 2] = '!';
			++index;
			all[index] = finalizers[index - letters.Length / 2] = '?';
			++index;
			all[index++] = ' ';
			all[index++] = ',';
			all[index++] = ';';
			all[index++] = ':';
		}
		char[] source = all;
		if (isFirst) source = letters;
		if (isLast) source = finalizers;
		char result = source[rng.Next(source.Length)];
		if (isFirst) result = char.ToUpper(result);
		return result;
	}
	#endregion

	#region Domain managing
	protected void EditDomain_Click(object sender, EventArgs e)
	{
		LinkButton eventRaisedBy = sender as LinkButton;
		System.Web.UI.ScriptManager.RegisterStartupScript(this, this.GetType(), "scrollBack", "scrollListTo('domainScroller', '" + eventRaisedBy.ClientID + "')", true);
		#region highlight row
		System.Web.UI.LiteralControl tr = (eventRaisedBy.Parent as RepeaterItem).Controls[0] as System.Web.UI.LiteralControl;
		tr.Text = tr.Text.Replace("class=\"tdn\"", "class=\"tdngris\"");
		#endregion
		SetDomainData(eventRaisedBy.CommandArgument);
		mdlPopupDomains.Show();
	}

	private void SetDomainData(string domainId)
	{
		bool missingId = string.IsNullOrEmpty(domainId);
		hiddenDomainId.Text = missingId ? "null" : domainId;
		string strDomainInfo = "nou";
		lblDomainTitle.Text = "Domeniu paraclinic " + (missingId ? strDomainInfo : "nr. " + domainId.PadLeft(7, '0'));
		#region Get Sectii:
		if (strSection.Items.Count == 0)
		{
			strSection.DataSource = Hipocrate.DB.DBConnection.GetData("Sections_Code", new string[] { "@fromLaboratory" }, new object[] { true });
			strSection.DataValueField = "code"; //TODO: Server.HTMLEncode in the DTable
			strSection.DataTextField = "Name";
			strSection.DataBind();
		}
		#endregion
		bool missingData = true;
		if (!missingId)
		{
			//set values from db
			//TODO? could replace with "Laborator_Generic_Select"
			DataTable domain = Hipocrate.DB.DBConnection.GetData("Laborator_Domain_Select", new string[] { "id" }, new object[] { domainId }).Tables[0];
			if (domain.Rows.Count > 0)
			{
				missingData = false;
				DataRow domainData = domain.Rows[0];
				strDomain.Text = strDomainInfo = domainData["Domain"].ToString();
				string selectedSection = domainData["Section"].ToString();
				if (strSection.Items.FindByValue(selectedSection) != null)
					strSection.SelectedValue = selectedSection;
				else
				{
					Tools.ScrieInLog(Server, "[Laborator_Domain_Select pt " + domainId + "] Sectia " + selectedSection + " nu a fost gasita in tabela!");
					//strSection.BackColor = System.Drawing.Color.Pink;
				}
				//SetMessage(selectedSection, false);
				strActive.Checked = System.Convert.ToBoolean(domainData["Active"]);
				strAlwaysVisible.Checked = System.Convert.ToBoolean(domainData["AlwaysVisible"]);

				if (!string.IsNullOrEmpty(domainData["DisplayAnalysesHeader"].ToString()))
					strDisplayAnalysesHeader.Checked = Convert.ToBoolean(domainData["DisplayAnalysesHeader"]);

				strScreenX.Text = domainData["ScreenX"].ToString();
				strScreenY.Text = domainData["ScreenY"].ToString();
				strScreenZ.Text = domainData["ScreenZ"].ToString();
				strPrintX.Text = domainData["PrintX"].ToString();
				strPrintY.Text = domainData["PrintY"].ToString();
				strPrintZ.Text = domainData["PrintZ"].ToString();
			}
		}
		if(missingData)
		{
			//clear all existing data:
			strDomain.Text = string.Empty;

			strActive.Checked = true;
			strAlwaysVisible.Checked = true;
			strDisplayAnalysesHeader.Checked = true;

			strScreenX.Text = strScreenY.Text = strScreenZ.Text = string.Empty;
			strPrintX.Text = strPrintY.Text = strPrintZ.Text = string.Empty;
		}
		lblDomainH2.Text = "Detalii pentru domeniul " + strDomainInfo;
		btnDeleteDomain.Visible = false;
		btnInsertDomain.Visible = !missingId;
	}

	protected void btnAddDomain_Click(object sender, EventArgs e)
	{
		SetDomainData("");
		mdlPopupDomains.Show();
	}

	protected void ProcessDomainCommand(object sender, EventArgs e)
	{
		try
		{
			object idParam = null;
			long id;
			if (long.TryParse(hiddenDomainId.Text, out id) && (id != 0))
				idParam = id; //else, it's left as null
			System.Collections.Generic.List<System.Data.SqlClient.SqlParameter> result =
			Hipocrate.DB.DBConnection.ExecuteSPWithInOutParams("Laborator_Domain_Manage",
				new string[] { "@strAction", "@intCodeID", "@strDomain", "@strSection",
				"@strActive", "@strAlwaysVisible", "@strDisplayAnalysesHeader", "@strCloseOnValidate",
				"@strScreenX", "@strScreenY", "@strScreenZ", "@strPrintX", "@strPrintY", "@strPrintZ" },
															//or: .Parent.ID[3] -- should result in either 'S' or 'D'
				new object[] { (sender as System.Web.UI.Control).NamingContainer.ID[3], idParam, strDomain.GetSqlEscapedString(), strSection.SelectedValue.SqlEscape(),
				strActive.Checked ? 1 : 0, strAlwaysVisible.Checked ? 1 : 0, strDisplayAnalysesHeader.Checked ? 1 : 0, 0,
				System.Convert.ToInt64(strScreenX.Text), System.Convert.ToInt64(strScreenY.Text), System.Convert.ToInt64(strScreenZ.Text), System.Convert.ToInt64(strPrintX.Text), System.Convert.ToInt64(strPrintY.Text), System.Convert.ToInt64(strPrintZ.Text) },
				//out parameters:
				"@strOUT" );
			object code = result[0].Value;
			if ((code == null) || string.IsNullOrEmpty(code.ToString()) || code.Equals(0))
				SetMessage("Domeniu actualizat.", false);
			else
				SetMessage("Sql Error Code: " + code.ToString(), true);
		}
		catch (Exception ex)
		{
			SetMessage(ex.Message, true);
			output.Text = Tools.HiddenExceptionDetails(ex);
		}
		ResetDomainId(null, null); //or: SetDomainData(null);
		mdlPopupDomains.Hide();
		LoadDomainData();
		up1.Update();

		//TODO: show details for db errors:

		//@ strAction = "S"
		//	if iRet <> 0 then _
		//		strError = "Inregistrarea curenta nu a fost salvata. Va rugam repetati actiunea!<br>Daca situatia persista, va rugam contactati administratorul aplicatiei."

		//@ strAction = "D"
		//	if cmd(0) != 0 or err.number != 0 then _
		//		strError = "Acest domeniu nu poate fi sters pentru ca are aparate atasate.<br>Trebuie sa stergeti mai intai aparatele atasate pentru a sterge domeniul."

		//	strError = "Database Error: " & err.Description
	}

	protected void ResetDomainId(object sender, EventArgs e)
	{
		SetDomainData(null);
	}

	protected void BackToDomainList(object sender, EventArgs e)
	{
		mdlPopupDomains.Hide();
	}
	#endregion

	#region AnalyseGroup managing
	protected void egvGroups_RowEditing(object sender, GridViewEditEventArgs e)
	{
		if (egvGroups.CurrentlyEditedRowsIndexes == null)
			egvGroups.CurrentlyEditedRowsIndexes = new System.Collections.Generic.List<int>();
		egvGroups.CurrentlyEditedRowsIndexes.Add(e.NewEditIndex);
		egvGroups.EnableEdit = true;
		egvGroups.Save(); //should save to the DataSet within the Session.
		egvGroups.DataBind(); //knows to retrieve the data from the Session...
		up1.Update();
	}

	protected void egvGroups_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
	{
		if (egvGroups.CurrentlyEditedRowsIndexes == null)
		{
			//make it so that it contains all the indexes of all the rows:
			egvGroups.CurrentlyEditedRowsIndexes = new System.Collections.Generic.List<int>();
			//if (egvGroups.EnableEdit) //of course this should be true, else how would the CancelEdit button be visible?
				for (int i = 0; i < egvGroups.Rows.Count; ++i)
					egvGroups.CurrentlyEditedRowsIndexes.Add(i);
		}
		egvGroups.CurrentlyEditedRowsIndexes.Remove(e.RowIndex);
		if (egvGroups.CurrentlyEditedRowsIndexes.Count == 0)
			egvGroups.EnableEdit = false;
		RefreshGroupData(false);
		egvGroups.DataBind(); //knows to retrieve the data from the Session...
		up1.Update();
	}

	protected void egvGroups_RowUpdating(object sender, GridViewUpdateEventArgs e)
	{
		ProcessGroupCommand('S', e.RowIndex);
		egvGroups.CurrentlyEditedRowsIndexes.Remove(e.RowIndex);
		if (egvGroups.CurrentlyEditedRowsIndexes.Count == 0)
			egvGroups.EnableEdit = false;
	}

	protected void egvGroups_RowDeleting(object sender, GridViewDeleteEventArgs e)
	{
		ProcessGroupCommand('D', e.RowIndex);
		forceReadFromDb = true;
	}

	protected void egvGroups_RowInserting(object sender, GridViewInsertEventArgs e)
	{
		ProcessGroupCommand('I', e.RowIndex);
		forceReadFromDb = true; //to retrieve the real ID!
	}

	private void ProcessGroupCommand(char action, int rowIndex) //action == 'I' iff egvGroups.IsNewRow(rowIndex)
	{
		//output.Text += "Action requested: " + action + " on " + rowIndex + "<br/>";
		try
		{
			object idParam = null;
			DataSet groupSet = this.Session[egvGroups.SessionKey] as DataSet;
			DataRow userInput = null;
			if (!egvGroups.IsNewRow(rowIndex))
			{
				idParam = groupSet.Tables[egvGroups.SessionKeyIndex].Rows[rowIndex][egvGroups.DataKeyNames[0]];
				//SetMessage("Found id: " + idParam.ToString(), false);
				userInput = groupSet.Tables[egvGroups.SessionKeyIndex].Rows[rowIndex];
			}
			else
				userInput = egvGroups.PreviewUserInput(rowIndex).Row;
			string strName = userInput["Name"].ToString().SqlEscape();
			int isMultiple = System.Convert.ToBoolean(userInput["Multiple"]) ? 1 : 0;
			int isActive = System.Convert.ToBoolean(userInput["Active"]) ? 1 : 0;
			System.Collections.Generic.List<System.Data.SqlClient.SqlParameter> result =
			Hipocrate.DB.DBConnection.ExecuteSPWithInOutParams("Laborator_AnalyseGroup_Manage",
				new string[] { "@strAction", "@intCodeID", "@strName", "@strMultiple", "@strActive" },
				new object[] { action, idParam, strName, isMultiple, isActive },
				//out parameters:
				"@strOUT");
			if ((result[0].Value != DBNull.Value) && !result[0].Value.Equals(0))
				SetMessage("Sql Error Code: " + result[0].Value.ToString(), true);
		}
		catch (Exception ex)
		{
			SetMessage(ex.Message, true);
			output.Text = Tools.HiddenExceptionDetails(ex);
		}
		//fully update the contents of the DataSet in the session:
		if (originalRows == null)
		{
			if (DebugMode) output.Text += "Had not saved the session yet(?!), calling SOSDS...";
			SaveOriginalSessionDataSet(LoadedGroupData);
		}
		if (DebugMode) output.Text += "originalRows contains: " + originalRows.Count;
		this.PreRender += new EventHandler(DomeniiAparate_PreRender); //the only thing that is guaranteed to be called after event handlers, iirc
	}

	private System.Collections.Generic.Dictionary<object, object[]> originalRows = null;
	void SaveOriginalSessionDataSet(DataTable source)
	{
		originalRows = new System.Collections.Generic.Dictionary<object, object[]>(source.Rows.Count);
		foreach (DataRow row in source.Rows)
			originalRows[row[egvGroups.DataKeyNames[0]]] = row.ItemArray;
	}
	bool CompareRows(ref System.Collections.Generic.List<int> filter)
	{
		return CompareRows(ref filter, null);
	}
	bool CompareRows(ref System.Collections.Generic.List<int> filter, DataTable currentTable)
	{
		if(currentTable == null)
			currentTable = (this.Session[egvGroups.SessionKey] as DataSet).Tables[egvGroups.SessionKeyIndex];
		if (originalRows == null)
			throw new ApplicationException("@@ " + new System.Diagnostics.StackTrace(true).ToString() + " ##");
		if (filter == null)
		{
			filter = new System.Collections.Generic.List<int>(originalRows.Count);
			for (int i = 0; i < originalRows.Count; ++i)
				filter.Add(i);
		}
		bool result = false;
		foreach (int rowNumber in filter)
		{
			object key = currentTable.Rows[rowNumber][egvGroups.DataKeyNames[0]];
			if (ItemArraysDiffer(originalRows[key], currentTable.Rows[rowNumber].ItemArray))
			{
				//output.Text += "! differing row @ " + rowNumber + " has RowState = " + currentTable.Rows[rowNumber].RowState.ToString();
				egvGroups.Rows[rowNumber].BackColor = System.Drawing.Color.Pink;
				result = true;
			}
			else //mini-bug: after a "back to editing", any new changes will HIDE the old ones!!!
				egvGroups.Rows[rowNumber].BackColor = System.Drawing.Color.LightYellow;
		}
		return result;
	}
	bool ItemArraysDiffer(object[] array1, object[] array2)
	{
		if (array1.Length != array2.Length) return true;
		for (int i = 0; i < array1.Length; ++i)
			if (!array1[i].Equals(array2[i]))
				return true;
		return false;
	}
	void CheckGridState()
	{
		bool hasChanged = false;
		bool triggerIsEditAllButton = IsPostBack && PostBackCause().Contains("$egvGroups$") && PostBackCause().EndsWith("$lbEdit");
		if (triggerIsEditAllButton) //Finish Editing
			if (egvGroups.CurrentlyEditedRowsIndexes != null)
			{
				//output.Text += "OnLoad: " + editedRowIndexes.Count;
				//output.Text += "Check: " + egvGroups.CurrentlyEditedRowsIndexes.Count;
				hasChanged = CompareRows(ref editedRowIndexes);
				//output.Text += "CompareRows returned: " + hasChanged.ToString();
			}
			else
				if (!egvGroups.EnableEdit)
				{
					//output.Text += "Finishing editing all at once!";
					editedRowIndexes = null; //will be filled by CompareRows
					hasChanged = CompareRows(ref editedRowIndexes);
				}
				//else
				//    output.Text += "Starting to edit all...";
		DataTable dt = (this.Session[egvGroups.SessionKey] as DataSet).Tables[egvGroups.SessionKeyIndex];
		if (DebugMode)
		{
			output.Text += "<br/><br/>";
			output.Text += "At preRender time:<br/>";
			DumpDataTable(dt);
			output.Text += "</td></tr></table>";
		}

		if (triggerIsEditAllButton && !hasChanged)
		{
			if (DebugMode) output.Text += "No change detected! (!) Local CERI contains: " + (editedRowIndexes == null ? 0 : editedRowIndexes.Count);
			//check RowStates: if any of them say "modified", read from database and compare AGAIN!
			bool anyModifications = false;
			foreach(DataRow row in dt.Rows)
				if (row.RowState == DataRowState.Modified)
				{
					anyModifications = true;
					break;
				}
			if (anyModifications)
				try
				{
					RefreshGroupData(true);
					hasChanged = CompareRows(ref editedRowIndexes, LoadedGroupData);
					if (DebugMode) output.Text += "After a trip to the db: Modi = " + hasChanged.ToString();
				}
				catch (Exception ex)
				{
					output.Text += "<br/><br/><br/>" + ex.ToString() + "<br/><br/><br/>";
					return;
				}
		}
		if (hasChanged)
		{
			//if(!egvGroups.EnableEdit)
				mdlPopupConfirmationRequest.Show();
			//keep the info:
			if (egvGroups.CurrentlyEditedRowsIndexes == null)
				egvGroups.CurrentlyEditedRowsIndexes = new System.Collections.Generic.List<int>();
			foreach (int index in editedRowIndexes)
				if (!egvGroups.CurrentlyEditedRowsIndexes.Contains(index))
					egvGroups.CurrentlyEditedRowsIndexes.Add(index);
			return;
		}

		RefreshGroupData(forceReadFromDb);
		up1.Update();
	}
	private bool forceReadFromDb = false;
	protected void ProcessGroupConfirmationAction(object sender, EventArgs e)
	{
		System.Web.UI.Control requestor = (sender as System.Web.UI.Control).NamingContainer; // for the HtmlInputButtons generated by the "CustomButton" tags
		if (sender is ImageButton) requestor = sender as ImageButton;
		char requestedAction = requestor.ID[3];
		bool shouldReadFromDb = false;
		switch (requestedAction)
		{
			case 'S': //save all
				if (egvGroups.CurrentlyEditedRowsIndexes == null) //fill it with all rowNumbers
				{
					egvGroups.CurrentlyEditedRowsIndexes = new System.Collections.Generic.List<int>();
					for (int i = 0; i < egvGroups.Rows.Count; ++i)
						egvGroups.CurrentlyEditedRowsIndexes.Add(i);
				}
				foreach (int rowNumber in egvGroups.CurrentlyEditedRowsIndexes)
					ProcessGroupCommand('S', rowNumber);
				egvGroups.CurrentlyEditedRowsIndexes.Clear();
				egvGroups.EnableEdit = false;
				shouldReadFromDb = true;
				break;
			case 'D': //discard all
				if(egvGroups.CurrentlyEditedRowsIndexes!=null)
					egvGroups.CurrentlyEditedRowsIndexes.Clear();
				editedRowIndexes.Clear();
				egvGroups.EnableEdit = false;
				shouldReadFromDb = true;
				break;
			case 'B': //back to editing
			case 'C': //close popup
				if (DebugMode) output.Text += "Should edit: " + egvGroups.CurrentlyEditedRowsIndexes.Count;
				egvGroups.EnableEdit = true;
				//NB: at this point, egvGroups.CurrentlyEditedRowsIndexes contains what it should: indexes of all the "in edit" rows
				//DebugMode = true;
				break;
			default:
				throw new ApplicationException("Unknown Group Action '" + requestedAction + "'!");
		}
		mdlPopupConfirmationRequest.Hide();
		RefreshGroupData(shouldReadFromDb);
		up1.Update();
	}
	#endregion

	#region Machine managing
	protected void EditMachine_Click(object sender, EventArgs e)
	{
		LinkButton eventRaisedBy = sender as LinkButton;
		System.Web.UI.ScriptManager.RegisterStartupScript(this, this.GetType(), "scrollBack", "scrollListTo('machineScroller', '" + eventRaisedBy.ClientID + "')", true);
		#region highlight row
		System.Web.UI.LiteralControl tr = (eventRaisedBy.Parent as RepeaterItem).Controls[0] as System.Web.UI.LiteralControl;
		tr.Text = tr.Text.Replace("class=\"tdn\"", "class=\"tdngris\"");
		#endregion
		SetMachineData(eventRaisedBy.CommandArgument);
		mdlPopupMachines.Show();
	}

	private void SetMachineData(string machineId)
	{
		bool missingId = string.IsNullOrEmpty(machineId);
		hiddenMachineId.Text = missingId ? "null" : machineId;
		string strMachineInfo = "nou";
		lblMachineTitle.Text = "Tip de aparat " + (missingId ? strMachineInfo : "nr. " + machineId.PadLeft(7, '0'));
		#region Get Clase Rezultat (i.e., Domenii Paraclinice!):
		if (strClass.Items.Count == 0)
		{
			//TODO? could replace with "Laborator_Generic_Select"
			strClass.DataSource = Hipocrate.DB.DBConnection.GetData("Laborator_Domain_Select", new object[] { });
			strClass.DataValueField = "domain"; //TODO: Server.HTMLEncode in the DTable
			strClass.DataTextField = "domain";
			strClass.DataBind();
		}
		#endregion
		bool missingData = true;
		if (!missingId)
		{
			//set values from db
			//TODO? could replace with "Laborator_Generic_Select"
			DataTable machine = Hipocrate.DB.DBConnection.GetData("Laborator_Machine_Select", new string[] { "id" }, new object[] { machineId }).Tables[0];
			if (machine.Rows.Count > 0)
			{
				missingData = false;
				DataRow machineData = machine.Rows[0];
				strMachineName.Text = strMachineInfo = machineData["Name"].ToString();
				string selectedClass = machineData["Class"].ToString();
				if (strClass.Items.FindByValue(selectedClass) != null)
					strClass.SelectedValue = selectedClass;
				else
				{
					Tools.ScrieInLog(Server, "[Laborator_Machine_Select pt " + machineId + "] Domeniul(Clasa) " + selectedClass + " nu a fost gasit(a) in tabela!");
					//strSection.BackColor = System.Drawing.Color.Pink;
				}
				//SetMessage(selectedSection, false);
				strSupplier.Text = machineData["Supplier"].ToString();
				strURL.Text = machineData["URL"].ToString();
				//strPhoto.Text = machineData["photo"].ToString();
				strReactiv.Text = machineData["Reactiv"].ToString();

				if (!string.IsNullOrEmpty(machineData["AfisezPeBuletin"].ToString()))
					strVisible.Checked = Convert.ToBoolean(machineData["AfisezPeBuletin"]);
			}
		}
		if (missingData)
		{
			//clear all existing data:
			strMachineName.Text = string.Empty;
			strSupplier.Text = strURL.Text = string.Empty;
			strReactiv.Text = string.Empty;
			strVisible.Checked = true;
		}
		lblMachineH2.Text = "Detalii pentru aparatul " + strMachineInfo;
		btnDeleteMachine.Visible = !missingId;
		btnInsertMachine.Visible = !missingId;
	}

	protected void btnAddMachine_Click(object sender, EventArgs e)
	{
		SetMachineData("");
		mdlPopupMachines.Show();
	}

	protected void ProcessMachineCommand(object sender, EventArgs e)
	{
		try
		{
			object idParam = null;
			long id;
			if (long.TryParse(hiddenMachineId.Text, out id) && (id != 0))
				idParam = id; //else, it's left as null
			char requestedAction = (sender as System.Web.UI.Control).NamingContainer.ID[3]; //or: .Parent.ID[3] -- should result in either 'S' or 'D'
			System.Collections.Generic.List<System.Data.SqlClient.SqlParameter> result =
			Hipocrate.DB.DBConnection.ExecuteSPWithInOutParams("Laborator_Machine_Manage",
				new string[] { "@strAction", "@intCodeID", "@strName", "@strClass",
					"@strSupplier", "@strURL", "@strReactiv", "@strAfisezPeBuletin" },
				new object[] { requestedAction, idParam, strMachineName.GetSqlEscapedString(), strClass.SelectedValue.SqlEscape(),
					strSupplier.GetSqlEscapedString(), strURL.GetSqlEscapedString(), strReactiv.GetSqlEscapedString(), strVisible.Checked ? "1" : "0" },
				//out parameters:
				"@strOUT");
			object code = result[0].Value;
			//TODO: show details for db errors!
			if ((code == null) || string.IsNullOrEmpty(code.ToString()) || code.Equals(0))
				SetMessage("Tip de aparat actualizat.", false);
			else
				if (requestedAction == 'D')
					SetMessage("Acest aparat nu mai poate fi sters din baza de date, pentru ca exista deja analize atasate.", true);
				else
					//if strAction = "S" and iRet <> 0 then strError = "Inregistrarea curenta nu a fost salvata. Va rugam repetati actiunea!<br>Daca situatia persista, va rugam contactati administratorul aplicatiei."
					SetMessage("Sql Error Code: " + code.ToString(), true);
		}
		catch (Exception ex)
		{
			SetMessage(ex.Message, true);
			output.Text = Tools.HiddenExceptionDetails(ex);
			/*
				if err.number = 0 then
					cn.CommitTrans
				else
					cn.RollbackTrans

					strError = "Database Error: " & err.Description
					err.Clear
				end if
			*/
		}
		ResetMachineId(null, null); //or: SetMachineData(null);
		//technically, if strAction = "S" and intCodeID = "0" and err.number = 0 then intCodeID = cmd("@strOUT")...
		mdlPopupMachines.Hide();
		LoadMachineData();
		up1.Update();
	}

	protected void ResetMachineId(object sender, EventArgs e)
	{
		SetMachineData(null);
	}

	protected void BackToMachineList(object sender, EventArgs e)
	{
		mdlPopupMachines.Hide();
	}
	#endregion

	#region debug
	private bool? forceDebugMode = null;
	private bool DebugMode
	{
		get { return (bool)(forceDebugMode ?? this.ViewState["DebugMode"] ?? false); }
		set { this.ViewState["DebugMode"] = forceDebugMode ?? value; }
	}
	private void DumpDataTableBegin()
	{
		output.Text += @"<table><tr><td>
								At page load:<br/>";
		if (egvGroups.DataSource == null)
			output.Text += "no DS in egvGroups (!)";
		else
			DumpDataTable((this.Session[egvGroups.SessionKey] as DataSet).Tables[egvGroups.SessionKeyIndex]);
		output.Text += "</td><td>";
	}
	private void DumpDataTable(DataTable dt)
	{
		output.Text += "<table>";
		string[] colNames = new string[dt.Columns.Count];
		int i = 0;
		output.Text += "<tr>";
		foreach (DataColumn c in dt.Columns)
		{
			output.Text += "<th>";
			output.Text += (colNames[i++] = c.ColumnName);
			output.Text += "</th>";
		}
		output.Text += "</tr>";
		foreach (DataRow dr in dt.Rows)
		{
			output.Text += "<tr>";
			for (int c = 0; c < colNames.Length; ++c )
			{
				output.Text += "<td>";
				output.Text += dr[colNames[c]];
				output.Text += "</td>";
			}
			output.Text += "</tr>";
			if (forceDebugMode != null) break;
		}
		output.Text += "</table>";
	}
	private void DumpDataTableEnd()
	{
		output.Text += "At render time:<br/>";
		DumpDataTable((this.Session[egvGroups.SessionKey] as DataSet).Tables[egvGroups.SessionKeyIndex]);
		output.Text += "</td></tr></table>";
	}
	private void ShowRowStates(string details)
	{
		output.Text += details;
		if(egvGroups.DataSource == null)
			output.Text += "no DS in egvGroups (!)";
		else
		{
			output.Text += "[";
			foreach (DataRow r in egvGroups.DataSource.Table.Rows)
				output.Text += r.RowState.ToString()[0];
			output.Text += "]";
		}
	}
	#endregion
}
